package com.express.gateway.verticle;

import com.express.gateway.common.CommonCons;
import io.vertx.core.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.express.gateway.common.util.StrUtil;
import com.express.gateway.common.EventBusTopicConst;

import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

import static com.express.gateway.common.CommonCons.KEY_CLUSTER;

/**
 * Exp-Inlet内部使用的客户端,既可以让他做操作Exp-Inlet相关
 *
 * @author flower
 */
public class ClientCliVerticle extends AbstractVerticle {
    private static final Logger LOG = LogManager.getLogger(ClientCliVerticle.class);


    @Override
    public void start(Promise<Void> startFuture) throws Exception {
        LOG.info("start CLI Verticle");
        vertx.eventBus().consumer(KEY_CLUSTER + EventBusTopicConst.CLI_START_EVERYTHING, this::startEverything);
        vertx.eventBus().consumer(KEY_CLUSTER + EventBusTopicConst.CLI_START_ALL_APP, this::startAllApp);
        vertx.eventBus().consumer(KEY_CLUSTER + EventBusTopicConst.CLI_START_APP_EVERYTHING, this::startAppEverything);
        vertx.eventBus().consumer(KEY_CLUSTER + EventBusTopicConst.CLI_START_APP, this::startApp);
        LOG.info("start CLI Verticle successful");
        super.start(startFuture);
    }

    /**
     * 启动所有网关应用与API
     *
     * @param msg
     */
    public void startEverything(Message<JsonObject> msg) {
        LOG.info("cli->执行启动所有网关应用与API...");
        vertx.executeBlocking(futrue -> {
            vertx.eventBus().<JsonArray>request(EventBusTopicConst.FIND_APP, null, reply -> {
                if (reply.failed()) {
                    LOG.error("cli->执行启动所有网关应用与API 失败:" + reply.cause());
                    futrue.fail(reply.cause());
                    return;
                }
                JsonArray body = reply.result().body();
                if (body == null || body.isEmpty()) {
                    LOG.info("cli->执行启动所有网关应用与API 结果:网关应用数量为:0");
                    futrue.complete();
                    return;
                }
                startAppService(body, 0, 0, true, handler -> {
                    Integer success = handler.result().getInteger("success", 0);
                    Integer fail = handler.result().getInteger("fail", 0);
                    LOG.info("cli->执行启动所有网关应用与API 结果:成功数量:" + success + ",失败数量:" + fail);
                    futrue.complete();
                });
            });
        }, result -> {
            if (result.succeeded()) {
                msg.reply("ok");
            } else {
                msg.reply("500");
                LOG.error("cli->执行启动所有网关应用与API 失败:" + result.cause());
            }
        });
    }

    /**
     * 启动所有网关应用
     *
     * @param msg
     */
    public void startAllApp(Message<JsonObject> msg) {
        LOG.info("cli->执行启动所有网关应用...");
        vertx.executeBlocking(() -> {
            vertx.eventBus().<JsonArray>request(EventBusTopicConst.FIND_APP, null, reply -> {
                if (reply.failed()) {
                    msg.reply("500");
                    LOG.error("cli->执行启动所有网关应用 失败:" + reply.cause());
                    return;
                }
                JsonArray body = reply.result().body();
                if (body == null || body.isEmpty()) {
                    LOG.info("cli->执行启动所有网关应用 结果:网关应用数量为:0");
                    msg.reply("ok");
                    return;
                }
                startAppService(body, 0, 0, true, handler -> {
                    Integer success = handler.result().getInteger(CommonCons.KEY_SUCCESS, 0);
                    Integer fail = handler.result().getInteger(CommonCons.KEY_FAIL, 0);
                    LOG.info("cli->执行启动所有网关应用 结果:成功数量:" + success + ",失败数量:" + fail);
                    msg.reply("ok");
                });
            });
            return null;
        }, true);
    }

    /**
     * 启动指定网关应用并启动所有的API
     *
     * @param msg
     */
    public void startAppEverything(Message<JsonArray> msg) {
        LOG.info("cli->执行启动指定网关应用并启动所有的API...");
        JsonArray body = paramCheck(msg);
        if (body == null) return;
        vertx.executeBlocking(future -> {
            doStartApp(future, body, true);
        }, result -> {
            doStartAppFailProc(result, msg);
        });
    }

    /**
     * 启动指定网关应用
     *
     * @param msg
     */
    public void startApp(Message<JsonArray> msg) {
        LOG.info("cli->执行启动指定网关应用...");
        JsonArray body = paramCheck(msg);
        if (body == null) return;
        vertx.executeBlocking(future -> {
            doStartApp(future, body, false);
        }, result -> {
            doStartAppFailProc(result, msg);
        });
    }

    private static JsonArray paramCheck(Message<JsonArray> msg) {
        JsonArray body = msg.body();
        if (body == null || body.isEmpty()) {
            msg.reply("1400");
            LOG.debug("cli->执行启动指定网关应用 失败:缺少参数,系统接收到信息:" + body);
            return null;
        }
        return body;
    }

    private void doStartApp(Promise<Object> future, JsonArray body, boolean startAll) {
        vertx.eventBus().<JsonArray>request(EventBusTopicConst.FIND_APP, null, reply -> {
            if (reply.failed()) {
                future.fail(reply.cause());
                LOG.error("cli->执行启动指定网关应用 失败:" + reply.cause());
                return;
            }
            JsonArray allApp = reply.result().body();
            if (allApp == null || allApp.isEmpty()) {
                future.complete();
                LOG.info("cli->执行启动指定网关应用 失败:没有查询到应用网关");
                return;
            }
            JsonArray apps = new JsonArray();
            allApp.forEach(va -> {
                JsonObject json = (JsonObject) va;
                String key = json.getString(CommonCons.KEY_APP_NAME, "");
                if (body.contains(key)) {
                    apps.add(json);
                }
            });
            if (apps.isEmpty()) {
                LOG.info("cli->执行启动指定网关应用 结果:没有查询到应用网关,请查看是否输入了不存在的APP名字或者是否存在空格或者大小写");
                future.complete();
                return;
            }
            startAppService(apps, 0, 0, startAll, handler -> {
                Integer success = handler.result().getInteger(CommonCons.KEY_SUCCESS, 0);
                Integer fail = handler.result().getInteger(CommonCons.KEY_FAIL, 0);
                LOG.info("cli->执行启动指定网关应用 结果:成功数量:" + success + ",失败数量:" + fail);
                future.complete();
            });
        });
    }

    private static void doStartAppFailProc(AsyncResult<Object> result, Message<JsonArray> msg) {
        if (result.succeeded()) {
            msg.reply("ok");
            return;
        }
        msg.reply("500");
        LOG.error("cli->执行启动指定网关应用并启动所有的API 失败:" + result.cause());
    }

    /**
     * 启动所有网关应用与API服务
     *
     * @param apps     应用集合
     * @param success  成功的数量
     * @param fail     失败的数量
     * @param startAll 是否启动API true启动false不启动
     * @param handler  返回json key : success成功的数量,fail失败的数量
     */
    public void startAppService(JsonArray apps, int success, int fail, boolean startAll, Handler<AsyncResult<JsonObject>> handler) {
        // 待启动的应用为空时则退出
        if (apps == null || apps.isEmpty()) {
            JsonObject result = new JsonObject();
            result.put(CommonCons.KEY_SUCCESS, success);
            result.put(CommonCons.KEY_FAIL, fail);
            handler.handle(Future.succeededFuture(result));
            return;
        }
        // 网关应用
        JsonObject body = (JsonObject) apps.remove(0);
        final String appName = body.getString(CommonCons.KEY_APP_NAME);
        if (appName == null) {
            startAppService(apps, success, fail + 1, startAll, handler);
            LOG.error("cli->执行启动网关应用 失败:获取不到应用的名字:" + body);
            return;
        }
        // 启动网关应用需要的配置信息
        JsonObject config = new JsonObject();
        config.put(CommonCons.KEY_APP, body);
        config.put(CommonCons.KEY_APP_NAME, appName);
        String startAppAddress = EventBusTopicConst.DEPLOY_APP_DEPLOY;
        vertx.eventBus().request(startAppAddress, config, res -> {
            if (res.failed()) {
                startAppService(apps, success, fail + 1, startAll, handler);
                LOG.error("cli->执行启动网关应用 失败:" + res.cause());
                return;
            }
            // 判断是否启动所有API
            if (startAll) {
                startAllApi(appName);
            }
            startAppService(apps, success + 1, fail, startAll, handler);
        });
    }

    /**
     * 启动所有API
     *
     * @param appName 应用的名称
     */
    public void startAllApi(String appName) {
        if (StrUtil.isNullOrEmpty(appName)) {
            LOG.debug("cli->执行启动网关应用->启动所有API 失败:应用名称为空");
            return;
        }
        // 获取所有API
        JsonObject message = new JsonObject().put(CommonCons.KEY_APP_NAME, appName);
        vertx.eventBus().<JsonArray>request(EventBusTopicConst.FIND_API_ALL, message, reply -> {
            if (reply.failed()) {
                LOG.error("cli->执行启动网关应用:" + appName + "->获取所有API 失败:" + reply.cause());
                return;
            }
            JsonArray apis = reply.result().body();
            DeliveryOptions option = new DeliveryOptions();
            option.setSendTimeout(200 * 301);
            JsonObject config = new JsonObject();
            config.put(CommonCons.KEY_APP_NAME, appName);
            config.put("apis", apis);
            String startApiAddress = EventBusTopicConst.DEPLOY_API_START_ALL;
            vertx.eventBus().<String>request(startApiAddress, config, option, res -> {
                if (res.succeeded()) {
                    LOG.debug("cli->执行启动网关应用:" + appName + "->启动所有API 成功");
                } else {
                    LOG.error("cli->执行启动网关应用:" + appName + "->启动所有API 失败:" + reply.cause());
                }
            });
        });
    }
}
